﻿#region Usings
using System;

using BK.Model;
using BK.Repositories.Abstract;

using Lib.General;
#endregion

namespace BK.BlusinessLogic
{
    public abstract class BaseExpensesBl<TExpense, TExpenseFile> : BaseBkEntity
        where TExpense : IImportedEntity
    {
        #region Readonly & Static Fields
        private readonly AccountsBl _accountsBl;
        private readonly CategoriesBl _categoriesBl;
        private readonly CurrenciesBl _currenciesBl;
        private readonly IExpensesFilesBl<TExpense, TExpenseFile> _expensesFilesBl;
        private readonly IExpensesRepository<TExpense> _expensesRepository;
        private readonly ImportStatusesBl _importStatusesBl;
        private readonly PaymentsBl _paymentsBl;
        private readonly TargetsBl _targetsBl;
        #endregion

        #region C'tors
        public BaseExpensesBl(IExpensesRepository<TExpense> expensesRepository,
            IImportStatusesRepository importStatusesRepository,
            IAccountsRepository accountsRepository,
            IExpensesFilesRepository<TExpense, TExpenseFile> expensesFilesRepository,
            ICategoriesRepository categoriesRepository,
            ICurrenciesRepository currenciesRepository,
            ITargetsRepository targetsRepository,
            IPaymentsRepository paymentsRepository)
        {
            _expensesRepository = expensesRepository;

            _importStatusesBl = new ImportStatusesBl(importStatusesRepository);
            _accountsBl = new AccountsBl(accountsRepository);

            _expensesFilesBl = CreateExpensesFilesBl(expensesFilesRepository);

            _categoriesBl = new CategoriesBl(categoriesRepository);
            _currenciesBl = new CurrenciesBl(currenciesRepository);
            _importStatusesBl = new ImportStatusesBl(importStatusesRepository);
            _targetsBl = new TargetsBl(targetsRepository);
            _paymentsBl = new PaymentsBl(paymentsRepository);
        }
        #endregion

        #region Instance Methods
        public TExpense[] GetAllUnprocessed()
        {
            return _expensesRepository.GetAllUnprocessed();
        }

        public void ImportBestExense(TExpense expense)
        {
            Payment payment = null;
            try
            {
                ImportedFile expenseFile = _expensesFilesBl.GetAnotherByExpense(expense);
                if (expenseFile != null)
                {
                    TieImportStatus(expense, ImportStatuses.AlreadyImported);
                }
                else
                {
                    User user = expense.UserDependedImportedFile.User;

                    Currency currency = _currenciesBl.GetBy(user.Id, expense.Currency);
                    if (currency == null)
                    {
                        currency = new Currency(user.Id, expense.Currency, expense.Currency);
                    }

                    Account account = _accountsBl.GetBy(user.Id, expense.Account);
                    if (account == null)
                    {
                        account = new Account(expense.Account, user, currency);
                    }

                    Category category = GetRootCategory(user, expense);
                    if (category == null)
                    {
                        category = CreateCategory(user, expense);
                    }

                    Target target = GetRootTarget(user, expense);
                    if (target == null)
                    {
                        target = CreateTarget(user, expense);
                    }

                    string description = null;
                    if (!string.IsNullOrEmpty(expense.Type))
                    {
                        description = string.Format("(Type: {0})", expense.Category);
                    }
                    if (!string.IsNullOrEmpty(expense.Note))
                    {
                        description = string.Format("{0} - {1}", description, expense.Note);
                    }

                    payment = new Payment
                        {
                            Account = account,
                            Category = category,
                            Target = target,
                            DateTime = expense.Date,
                            Amount = expense.Amount,
                            Description = description,
                            User = user
                        };
                    TieImportStatus(expense, ImportStatuses.Successfull);
                }
            }
            catch (Exception ex)
            {
                TieImportStatus(expense, ImportStatuses.Fail);
                Logger.Error(GetType(), ex);
            }
            expense.Processed = true;
            Save(expense);
            if (payment != null)
            {
                _paymentsBl.Save(payment);
            }
        }

        public void Save(TExpense bestExpense)
        {
            _expensesRepository.Save(bestExpense);
        }

        public void TieImportStatus(TExpense expense, ImportStatuses importStatusToBeTied)
        {
            ImportStatus importStatus = _importStatusesBl.GetBy(importStatusToBeTied);
            if (importStatus == null)
            {
                importStatus = _importStatusesBl.Add(importStatusToBeTied);
            }
            expense.ImportStatuses.Add(importStatus);
        }

        protected abstract IExpensesFilesBl<TExpense, TExpenseFile> CreateExpensesFilesBl(
            IExpensesFilesRepository<TExpense, TExpenseFile> expensesFilesRepository);

        protected abstract Target GetRootTarget(User user, TExpense expense);

        protected virtual Category CreateCategory(User user, TExpense expense)
        {
            return new Category
                {
                    Name = expense.Category,
                    Parent = null,
                    User = user
                };
        }

        protected virtual Target CreateTarget(User user, TExpense expense)
        {
            return new Target
                {
                    Name = expense.Category,
                    Parent = null,
                    User = user
                };
        }

        protected virtual Category GetRootCategory(User user, TExpense expense)
        {
            return _categoriesBl.GetBy(user.Id, expense.Category, null);
        }
        #endregion

        #region Instance Properties
        protected CategoriesBl CategoriesBl
        {
            get { return _categoriesBl; }
        }

        protected TargetsBl TargetsBl
        {
            get { return _targetsBl; }
        }
        #endregion
    }
}